package edu.thu.studio.ormdesigner.editor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.EventObject;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.KeyHandler;
import org.eclipse.gef.KeyStroke;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.MouseWheelHandler;
import org.eclipse.gef.MouseWheelZoomHandler;
import org.eclipse.gef.SnapToGeometry;
import org.eclipse.gef.SnapToGrid;
import org.eclipse.gef.dnd.TemplateTransferDragSourceListener;
import org.eclipse.gef.dnd.TemplateTransferDropTargetListener;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.DirectEditAction;
import org.eclipse.gef.ui.actions.SelectionAction;
import org.eclipse.gef.ui.actions.ToggleGridAction;
import org.eclipse.gef.ui.actions.ToggleSnapToGeometryAction;
import org.eclipse.gef.ui.actions.ZoomInAction;
import org.eclipse.gef.ui.actions.ZoomOutAction;
import org.eclipse.gef.ui.parts.GraphicalEditorWithPalette;
import org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import edu.thu.studio.ormdesigner.editor.action.CopyAction;
import edu.thu.studio.ormdesigner.editor.action.DeleteWithoutUpdateAction;
import edu.thu.studio.ormdesigner.editor.action.PasteAction;
import edu.thu.studio.ormdesigner.editor.action.ShortCutAction;
import edu.thu.studio.ormdesigner.editor.action.ZoomAdjustAction;
import edu.thu.studio.ormdesigner.editor.contributor.ORMModelActionBarContributor;
import edu.thu.studio.ormdesigner.model.diagram.Diagram;
import edu.thu.studio.ormdesigner.outline.OutlinePage;
import edu.thu.studio.ormdesigner.util.ColorUtils;

public class ORMGraphicalEditor extends GraphicalEditorWithPalette {
    
    private EditorEditPartFactory editPartFactory;
    
    private OutlinePage outlinePage;
    
    private Diagram diagram;
    
    private PaletteRoot paletteRoot;
    
    private boolean isDirty;
    
    private ORMModelActionBarContributor actionBarContributor;
    
    public ORMGraphicalEditor(Diagram diagram, EditorEditPartFactory editPartFactory, OutlinePage outlinePage,
        DefaultEditDomain editDomain, PaletteRoot paletteRoot) {
        this.diagram = diagram;
        this.editPartFactory = editPartFactory;
        this.outlinePage = outlinePage;
        this.paletteRoot = paletteRoot;
        
        this.setEditDomain(editDomain);
        
    }
    
    @Override
    public void doSave(IProgressMonitor monitor) {
        this.getCommandStack().markSaveLocation();
        this.isDirty = false;
    }
    
    public void resetCommandStack() {
        this.getCommandStack().flush();
    }
    
    @Override
    public void commandStackChanged(EventObject eventObject) {
        this.firePropertyChange(IEditorPart.PROP_DIRTY);
        super.commandStackChanged(eventObject);
    }
    
    protected void initializeGraphicalViewer() {
        getGraphicalViewer().setContents(this.diagram);
        getGraphicalViewer().addDropTargetListener(new TemplateTransferDropTargetListener(getGraphicalViewer()));
    }
    
    protected void initializePaletteViewer() {
        super.initializePaletteViewer();
        getPaletteViewer().addDragSourceListener(new TemplateTransferDragSourceListener(getPaletteViewer()));
    }
    
    @Override
    protected void configureGraphicalViewer() {
        super.configureGraphicalViewer();
        
        GraphicalViewer viewer = this.getGraphicalViewer();
        
        viewer.setEditPartFactory(editPartFactory);
        viewer.setRootEditPart(new ScalableFreeformRootEditPart());
        
        viewer.setProperty(MouseWheelHandler.KeyGenerator.getKey(SWT.MOD1), MouseWheelZoomHandler.SINGLETON);
        viewer.setProperty(SnapToGrid.PROPERTY_GRID_ENABLED, true);
        viewer.setProperty(SnapToGrid.PROPERTY_GRID_VISIBLE, true);
        viewer.setProperty(SnapToGeometry.PROPERTY_SNAP_ENABLED, true);
        
        this.initViewerAction(viewer);
        
    }
    
    @Override
    protected PaletteRoot getPaletteRoot() {
        return this.paletteRoot;
    }
    
    @Override
    @SuppressWarnings("rawtypes")
    public Object getAdapter(Class type) {
        if (type == ActionRegistry.class) {
            return getActionRegistry();
        }
        if (type == ZoomManager.class) {
            return ((ScalableFreeformRootEditPart) getGraphicalViewer().getRootEditPart()).getZoomManager();
        }
        if (type == IContentOutlinePage.class) {
            return this.outlinePage;
        }
        return super.getAdapter(type);
    }
    
    public void removeSelection() {
        this.getGraphicalViewer().deselectAll();
    }
    
    @Override
    @SuppressWarnings("unchecked")
    protected void createActions() {
        super.createActions();
        
        ActionRegistry registry = this.getActionRegistry();
        List<String> selectionActionList = this.getSelectionActions();
        
        List<IAction> actionList = new ArrayList<IAction>(Arrays.asList(new IAction[] {
            new DirectEditAction((IWorkbenchPart) this), new DeleteWithoutUpdateAction(this), new CopyAction(this),
            new PasteAction(this), new ShortCutAction(this)}));
        
        for (IAction action : actionList) {
            if (action instanceof SelectionAction) {
                IAction originalAction = registry.getAction(action.getId());
                
                if (originalAction != null) {
                    selectionActionList.remove(originalAction);
                }
                selectionActionList.add(action.getId());
            }
            
            registry.registerAction(action);
        }
        
    }
    
    private void initViewerAction(GraphicalViewer viewer) {
        ScalableFreeformRootEditPart rootEditPart = new ScalableFreeformRootEditPart();
        viewer.setRootEditPart(rootEditPart);
        
        ZoomManager manager = rootEditPart.getZoomManager();
        
        double[] zoomLevels = new double[] {0.1, 0.25, 0.5, 0.75, 0.8, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 10.0, 20.0};
        manager.setZoomLevels(zoomLevels);
        
        List<String> zoomContributions = new ArrayList<String>();
        zoomContributions.add(ZoomManager.FIT_ALL);
        zoomContributions.add(ZoomManager.FIT_HEIGHT);
        zoomContributions.add(ZoomManager.FIT_WIDTH);
        manager.setZoomLevelContributions(zoomContributions);
        
        ZoomInAction zoomInAction = new ZoomInAction(manager);
        ZoomOutAction zoomOutAction = new ZoomOutAction(manager);
        ZoomAdjustAction zoomAdjustAction = new ZoomAdjustAction(manager);
        
        this.getActionRegistry().registerAction(zoomInAction);
        this.getActionRegistry().registerAction(zoomOutAction);
        this.getActionRegistry().registerAction(zoomAdjustAction);
        
        this.addKeyHandler(zoomInAction);
        this.addKeyHandler(zoomOutAction);
        
        IFigure gridLayer = rootEditPart.getLayer(LayerConstants.GRID_LAYER);
        gridLayer.setForegroundColor(ColorUtils.GRID_COLOR);
        
        IAction action = new ToggleGridAction(viewer);
        this.getActionRegistry().registerAction(action);
        
        action = new ToggleSnapToGeometryAction(viewer);
        this.getActionRegistry().registerAction(action);
        
        // 注册 ctrl+k 快照快捷键
        this.registerCtrlk(viewer);
        
        this.initActionBarContributor();
        
    }
    
    private void initActionBarContributor() {
        this.actionBarContributor = new ORMModelActionBarContributor();
        this.actionBarContributor.init(this.getEditorSite().getActionBars(), this.getSite().getPage());
    }
    
    private void addKeyHandler(IAction action) {
        IHandlerService service = (IHandlerService) this.getSite().getService(IHandlerService.class);
        service.activateHandler(action.getActionDefinitionId(), new ActionHandler(action));
    }
    
    private void registerCtrlk(GraphicalViewer viewer) {
        KeyHandler keyHandler = new KeyHandler();
        viewer.setKeyHandler(new GraphicalViewerKeyHandler(viewer).setParent(keyHandler));
        keyHandler.put(KeyStroke.getReleased((char) 11, (int) 'k', SWT.CTRL),
            this.getActionRegistry().getAction(ShortCutAction.ID));
    }
    
    @Override
    public GraphicalViewer getGraphicalViewer() {
        return super.getGraphicalViewer();
    }
    
    public void resetEditDomain() {
        this.getEditDomain().setPaletteViewer(this.getPaletteViewer());
    }
    
    @Override
    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
        IEditorPart editorPart = getSite().getPage().getActiveEditor();
        
        if (editorPart instanceof ORMMultiPageEditor) {
            ORMMultiPageEditor multiPageEditorPart = (ORMMultiPageEditor) editorPart;
            
            if (this.equals(multiPageEditorPart.getActiveEditor())) {
                updateActions(this.getSelectionActions());
            }
            
        } else {
            super.selectionChanged(part, selection);
        }
    }
    
    public Point getLocation() {
        FigureCanvas canvas = (FigureCanvas) this.getGraphicalViewer().getControl();
        return canvas.getViewport().getViewLocation();
    }
    
    public void setLocation(int x, int y) {
        FigureCanvas canvas = (FigureCanvas) this.getGraphicalViewer().getControl();
        canvas.scrollTo(x, y);
    }
    
    public void setDirty(boolean isDirty) {
        this.isDirty = isDirty;
    }
    
    @Override
    public boolean isDirty() {
        if (this.isDirty) {
            return true;
        }
        return super.isDirty();
    }
    
    public String getProjectFilePath(String extention) {
        IFile file = ((IFileEditorInput) this.getEditorInput()).getFile();
        String filePath = file.getLocation().toOSString();
        filePath = filePath.substring(0, filePath.lastIndexOf(".")) + extention;
        
        return filePath;
    }
    
    public ORMModelActionBarContributor getActionBarContributor() {
        return actionBarContributor;
    }
    
}
